package org.biogroovy.io;

import groovy.util.logging.Slf4j

import javax.xml.namespace.QName
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory



/**
 * This class contains methods used to parse an XML-based document and 
 * return a POJO.
 * 
 *
 * @param <P> the POJO class.
 */
@Slf4j
public abstract class AbsXmlReader<P> extends AbsReader<P, org.w3c.dom.Node> {

    /**
     * This method parses elements defined in the xpathMap from the rootNode.
     * @param rootNode	The root node of the document.
     * @param model		The model object being that will be filled. (i.e. a Gene object).
     * @param xpathMap	A map whose keys are the names of the model properties, and whose values are 
     * 					the XPath's required to parse out these objects.
     * @param nodeTypeMap
     * 					A map whose keys are the names of the model properties and whose values are
     * 					QName constants from the XPathConstants class that indicate the type of node to parsed by the XPath expression
     */
    void parseData(def rootNode, P model, Map<String, String> xpathMap, Map<String, QName> nodeTypeMap){
        Map propertiesMap = model.getProperties();
        //def nuPropMap = new HashMap<String, Object>();

        propertiesMap.each(){ key,value ->
            if (xpathMap.containsKey(key) && nodeTypeMap.containsKey(key)){
                def currval = null;
                if (nodeTypeMap.get(key).equals(XPathConstants.STRING)){
                    currval = parseString(rootNode, xpathMap.get(key));
                    model.setProperty(key, currval)
                }else if (nodeTypeMap.get(key).equals(XPathConstants.NUMBER)){
                    currval = parseNumber(rootNode, xpathMap.get(key))
                    model.setProperty(key, currval)
                }else if (nodeTypeMap.get(key).equals(XPathConstants.NODESET)){
                    currval = parseList(rootNode, xpathMap.get(key))
                    model.setProperty(key, currval)
                }
                log.debug("${key}:${currval}")
            }
        }
		
    }

    /**
     * This method parses a single string value using an xpath expression.
     * @param rootNode  The root node of the document.
     * @param xpathStr  The xpath expression.
     */
    String parseString(def rootNode, String xpathStr){
        def xpath = XPathFactory.newInstance().newXPath();
        return xpath.evaluate( xpathStr, rootNode, XPathConstants.STRING );
    }

    /**
     * This method parses a single numeric value using an xpath expression.
     * @param rootNode  The root node of the document.
     * @param xpathStr  The xpath expression.
     */
    Number parseNumber(def rootNode, String xpathStr){
        def xpath = XPathFactory.newInstance().newXPath();
        Number num = null;
        try{
            num = xpath.evaluate( xpathStr, rootNode, XPathConstants.NUMBER );
        }catch (Exception ex){
            log.debug("Error -- XPath = ${xpathStr}")
            throw ex;
        }
        return num
    }

    /**
     * This method parses a list of String values using an xpath expression.
     * @param rootNode  The root node of the document.
     * @param xpathStr  The xpath expression.
     */
    List<String> parseList(def rootNode, String xpathStr){
        def xpath = XPathFactory.newInstance().newXPath();
        def nodeSet =  xpath.evaluate( xpathStr, rootNode, XPathConstants.NODESET );
        List<String> list = new ArrayList<String>();
        nodeSet.each{
            log.debug(it.toString()); list.add( it.getFirstChild()?.getNodeValue())
        }
        nodeSet = null;
        return list;
    }


}
